package com.zxl.admin.service;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.github.pagehelper.PageHelper;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.ScheduledUpdateCertificatesVerifier;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;
import com.wechat.pay.contrib.apache.httpclient.util.AesUtil;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import com.zxl.common.constant.order.OrderStatusConstant;
import com.zxl.common.exception.BusinessException;
import com.zxl.common.mapper.SysOrderGoodsMapper;
import com.zxl.common.mapper.SysOrderLogisticsMapper;
import com.zxl.common.mapper.SysOrderMapper;
import com.zxl.common.model.WxV3payConfig;
import com.zxl.common.model.bo.order.OrderStatusBo;
import com.zxl.common.model.pojo.SysOrder;
import com.zxl.common.model.pojo.SysOrderLogistics;
import com.zxl.common.model.query.order.OrderPageQuery;
import com.zxl.common.model.query.order.RefundQuery;
import com.zxl.common.model.vo.order.OrderDetailsVo;
import com.zxl.common.model.vo.order.OrderGoodsDetailVo;
import com.zxl.common.model.vo.order.OrderPageVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.util.EntityUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.security.PrivateKey;
import java.util.Date;
import java.util.List;
import java.util.Objects;

/**
 * @author zxw
 * @date 2023/4/1
 */
@Service
@Slf4j
public class OrderService {

    @Resource
    private SysOrderMapper sysOrderMapper;
    @Resource
    private SysOrderLogisticsMapper sysOrderLogisticsMapper;
    @Resource
    private SysOrderGoodsMapper sysOrderGoodsMapper;
    @Resource
    private WxV3payConfig wxV3payConfig;

    public List<OrderPageVo> getOrderPage(OrderPageQuery query) {
        if (query.getOrderStatus() != null) {
            if (query.getOrderStatus().equals(0)) {
                query.setOrderStatus(null);
            }
        }
        PageHelper.startPage(query.getPageNumber(), query.getPageSize());
        return sysOrderMapper.getOrderList(query);
    }


    public void updateOrderStatus(OrderStatusBo orderStatusBo) {
        SysOrder order = sysOrderMapper.selectById(orderStatusBo.getId());
        if (!OrderStatusConstant.ORDER_STATUS_WAITPAY.getCode().equals(order.getOrderStatus())) {
            throw new BusinessException("订单状态错误,请刷新查看");
        }
        SysOrder sysOrder = new SysOrder();
        sysOrder.setId(orderStatusBo.getId());
        sysOrder.setOrderStatus(orderStatusBo.getOrderStatus());
        sysOrderMapper.updateById(sysOrder);
    }


    @Transactional(rollbackFor = Exception.class)
    public void sendOrder(SysOrderLogistics sysOrderLogistics) {
        SysOrder order = sysOrderMapper.selectById(sysOrderLogistics.getId());
        if (!OrderStatusConstant.ORDER_STATUS_WAITSEND.getCode().equals(order.getOrderStatus())) {
            throw new BusinessException("订单状态错误,请刷新查看");
        }
        String orderCode = sysOrderLogistics.getOrderCode();
        sysOrderLogisticsMapper.insert(sysOrderLogistics);
        sysOrderMapper.update(null, new UpdateWrapper<SysOrder>()
                .eq("order_code", orderCode)
                .set("order_status", OrderStatusConstant.ORDER_STATUS_WAITDELIVERY.getCode()));
    }

    public OrderDetailsVo getOrderDetail(String orderCode) {
        OrderDetailsVo orderDetails = sysOrderMapper.getOrderDetails(orderCode);
        List<OrderGoodsDetailVo> list = sysOrderGoodsMapper.list(orderCode);
        orderDetails.setOrderGoodsDetailVo(list);
        return orderDetails;
    }


    public boolean refund(RefundQuery query) {
        SysOrder sysOrder = sysOrderMapper.selectOne(new QueryWrapper<SysOrder>()
                .eq("order_code", query.getOrderCode()));
        this.validRefund(sysOrder);

        String out_trade_no = sysOrder.getOrderCode();
        String out_refund_no = sysOrder.getOrderCode().replace("C", "S");
        Integer orderAmt = sysOrder.getPayPrice().multiply(new BigDecimal("100")).intValue();

        try {
            JSONObject order = new JSONObject();
            order.put("out_trade_no", out_trade_no);//商户订单号
            order.put("out_refund_no", out_refund_no);//商户退款单号
            order.put("notify_url", "https://j.kaletcg.com/lzmtestadmin/admin/order/refundNotice");//退款通知

            JSONObject amount = new JSONObject();
            amount.put("refund", orderAmt);//退款金额
            amount.put("currency", "CNY");
            amount.put("total", orderAmt);//原订单金额
            order.put("amount", amount);

            PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(Objects.requireNonNull(getClass().getResourceAsStream("/appletPrivateKey.pem")));
            // 使用定时更新的签名验证器，不需要传入证书
            ScheduledUpdateCertificatesVerifier verifier = new ScheduledUpdateCertificatesVerifier(
                    new WechatPay2Credentials(wxV3payConfig.getAppletMchid(), new PrivateKeySigner(wxV3payConfig.getAppletMchSerialNo(), merchantPrivateKey)),
                    wxV3payConfig.getAppletApiV3Key().getBytes(StandardCharsets.UTF_8));
            WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
                    .withMerchant(wxV3payConfig.getAppletMchid(), wxV3payConfig.getAppletMchSerialNo(), merchantPrivateKey)
                    .withValidator(new WechatPay2Validator(verifier));

            // 通过WechatPayHttpClientBuilder构造的HttpClient，会自动的处理签名和验签，并进行证书自动更新
            HttpClient httpClient = builder.build();
            HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/refund/domestic/refunds");
            httpPost.addHeader("Accept", "application/json");
            httpPost.addHeader("Content-type", "application/json; charset=utf-8");
            httpPost.setEntity(new StringEntity(order.toJSONString(), "UTF-8"));
            // 后面跟使用Apache HttpClient一样
            HttpResponse response = httpClient.execute(httpPost);
            String bodyAsString = EntityUtils.toString(response.getEntity());
            JSONObject bodyAsJSON = JSONObject.parseObject(bodyAsString);
            log.info("退款信息:"  + bodyAsJSON.toJSONString());

            String status = bodyAsJSON.getString("status");
            if (status.equals("SUCCESS") || status.equals("PROCESSING")){
                sysOrder.setOrderStatus(OrderStatusConstant.ORDER_STATUS_REFUND.getCode());
                sysOrder.setRefundCode(out_refund_no);
                sysOrder.setRefundTime(new Date());
                sysOrderMapper.updateById(sysOrder);
                return true;
            }
        } catch (Exception e) {
            log.error(e.toString());
            e.printStackTrace();
        }
        return false;
    }

    public void validRefund(SysOrder sysOrder) {
        if (sysOrder == null) {
            throw new BusinessException("订单不存在");
        }

        if (!OrderStatusConstant.ORDER_STATUS_WAITSEND.getCode().equals(sysOrder.getOrderStatus())) {
            throw new BusinessException("待发货订单才能退款");
        }

    }

    public void refundNotice(JSONObject json) throws Exception {
        String ciphertext = json.getJSONObject("resource").getString("ciphertext");
        final String associated_data = json.getJSONObject("resource").getString("associated_data");
        final String nonce = json.getJSONObject("resource").getString("nonce");
        AesUtil aesUtil = new AesUtil(wxV3payConfig.getAppletApiV3Key().getBytes(StandardCharsets.UTF_8));
        ciphertext = aesUtil.decryptToString(associated_data.getBytes(), nonce.getBytes(), ciphertext);
        JSONObject jsonObject = JSONObject.parseObject(ciphertext);
        log.info("回调信息" + jsonObject);
    }
}
